package com.room.search.security;

import com.room.search.constant.Constants;
import com.room.search.core.redis.RedisCache;
import com.room.search.domain.RoomUser;
import com.room.search.domain.model.LoginUser;
import com.room.search.repository.RoomUserRepository;
import com.room.search.util.RegexCheckUtil;
import com.room.search.util.ServletUtils;
import com.room.search.util.ip.AddressUtils;
import com.room.search.util.ip.IpUtils;
import eu.bitwalker.useragentutils.UserAgent;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * Authenticate a user from the database.
 */
@Component("userDetailsService")
public class DomainUserDetailsService implements UserDetailsService {

    private final Logger log = LoggerFactory.getLogger(DomainUserDetailsService.class);

    private final RoomUserRepository userRepository;
    private final RedisCache redisCache;

    private static final Integer EXPIRE_TIME = 30;

    public DomainUserDetailsService(RoomUserRepository userRepository, RedisCache redisCache) {
        this.userRepository = userRepository;
        this.redisCache = redisCache;
    }

    @Override
    @Transactional
    public UserDetails loadUserByUsername(final String login) {
        log.debug("Authenticating {}", login);


        if (!RegexCheckUtil.checkMobile(login)) {
            return userRepository
                .findByAccount(login)
                .map(user -> createSpringSecurityUser(login, user))
                .orElseThrow(() -> new UsernameNotFoundException("尚未注册，请注册！"));
        }

        String lowercaseLogin = login.toLowerCase(Locale.ENGLISH);
        return userRepository
            .findOneWithAuthoritiesByAccount(lowercaseLogin)
            .map(user -> createSpringSecurityUser(lowercaseLogin, user))
            .orElseThrow(() -> new UsernameNotFoundException("未找到该用户！"));
    }

    private UserDetails createSpringSecurityUser(String lowercaseLogin, RoomUser roomUser) {

//        在线用户保存的Key
        String key = Constants.LOGIN_USER_KEY + lowercaseLogin;
        LoginUser onlineUser = getOnlineUser(roomUser);
        redisCache.setCacheObject(key, onlineUser, EXPIRE_TIME, TimeUnit.MINUTES);

        if (!roomUser.isActivated()) {
            throw new UserNotActivatedException("User ：" + lowercaseLogin + " 不可用");
        }
        List<GrantedAuthority> grantedAuthorities = roomUser
            .getAuthorities()
            .stream()
            .map(authority -> new SimpleGrantedAuthority(authority.getName()))
            .collect(Collectors.toList());
        return new org.springframework.security.core.userdetails.User(roomUser.getAccount(), roomUser.getPassword(), grantedAuthorities);
    }

    private LoginUser getOnlineUser(RoomUser roomUser) {
        UserAgent userAgent = UserAgent.parseUserAgentString(ServletUtils.getRequest().getHeader("User-Agent"));
        LoginUser user = new LoginUser();
        String ipAddr = IpUtils.getIpAddr(ServletUtils.getRequest());
        user.setUsername(roomUser.getNickName()).setLoginTime(new Date()).setRoomUser(roomUser)
            .setIpaddr(ipAddr)
            .setBrowser(userAgent.getBrowser().getName())
            .setOs(userAgent.getOperatingSystem().getName())
            .setLoginLocation(AddressUtils.getRealAddressByIP(ipAddr));
        return user;
    }
}
